defmodule Pango.DeeprList do
	@moduledoc """

	"""

	import  UserLib
	require ProjectType
	require Logger
	@default %{"list" => [], "total_page" => 0, "total_count" => 0}

	def list(user, args, caller_mod, func) do
		{repo, sql, pg_params, cypher, cypher_params, obj_keys, result} =
			Pango.DeeprParse.parse_query(args, user, func, caller_mod)
		resp = cypher_execute(
			cypher,
			cypher_params,
			psql_execute(sql, repo, pg_params, user, caller_mod, args, result),
			user,
			args,
			caller_mod
		)
		Enum.reduce(
			args,
			resp,
			fn ({obj, h}, acc) ->
				case Map.get(resp, obj) do
					nil ->
						Map.put(acc, obj, Map.merge(Map.take(h, ["[]"]), @default))
					v ->
						acc
				end
			end
		)
	end

	def psql_execute(:first, _repo, _pg_params, _user, _, _, result) do
		result
	end
	def psql_execute(sql, repo, pg_params, user, caller_mod, args, result) do
		pgsql_query(repo, pg_params, sql)
		|> ListKit.reduce_list(
				 result,
				 fn ([object, method, page, total_count, total_page, rows | rest], acc) ->
					 mod = caller_mod.get_db_mod(object)
					 rows = if rows == nil do
						 []
					 else
						 rows
					 end
					 rows = DeeprHelpLib.match(
						 caller_mod,
						 user,
						 rows,
						 get_rel(mod, object, args, "optional"),
						 get_rel(mod, object, args, "match"),
						 mod.foreign()
					 )
					 {rows, new_map} =
						 case Map.get(mod.pipelines(), method) do
							 {m, f, a} ->
								 case apply(m, f, [:after, user, object, rows | a]) do
									 r = {_new_list, _new} ->
										 r
									 new_list ->
										 {new_list, %{}}
								 end
							 _ ->
								 {rows, %{}}
						 end
					 rows = DeeprHelpLib.guard(rows, mod, MapLib.get_in(args, [object, "=>"]))
					 v2 = %{"total_page" => total_page, method => rows, "[]" => page, "total_count" => total_count}
								|> Map.merge(new_map)
					 acc = Map.put(acc, object, v2)
					 {rest, acc}
				 end
			 )
	end


	def cypher_execute(:first, _cypher_params, result, _, _, _) do
		result
	end
	def cypher_execute(cypher, cypher_params, result, user, args, caller_mod) do
		cypher
		|> Neo4jDb.get_all(cypher_params)
		|> Enum.reduce(
				 result,
				 fn
					 # list
					 %{"list" => list, "object" => object, "total_count" => total_count, "page" => page} = h, acc ->
						 mod = caller_mod.get_db_mod(object)
						 {list, new_map} = if list == nil do
							 {[], %{}}
						 else
							 list = DeeprHelpLib.guard(list, mod, get_select(object, args))
							 list = DeeprHelpLib.match(
								 caller_mod,
								 user,
								 list,
								 get_rel(mod, object, args, "optional"),
								 get_rel(mod, object, args, "match"),
								 mod.foreign()
							 )
							 case Map.get(mod.pipelines(), "list") do
								 {m, f, a} ->
									 case apply(
													m,
													f,
													[
														:after,
														user,
														object,
														list | a
													]
												) do
										 r = {_new_list, _new} ->
											 r
										 new_list ->
											 {new_list, %{}}
									 end
								 _ ->
									 {list, %{}}
							 end
						 end
						 Map.put(
							 acc,
							 object,
							 Map.drop(h, ["object", "page"])
							 |> Map.put("list", list)
							 |> Map.put("[]", page)
							 |> Map.put("total_count", total_count)
							 |> Map.merge(new_map)
						 )
					 # info
					 %{"object" => object} = h, acc ->
						 Map.put(acc, object, Map.delete(h, "object"))
				 end
			 )
	end

	def pgsql_query({m, f, a}, pg_params, sql) do
		case apply(m, f, [sql, pg_params | a]) do
			{:ok, %{rows: []}} ->
				[]
			{:ok, %{rows: [rows]}} ->
				rows
			{:error, reason} ->
				Logger.error("Deepr List Pg Execute Error, reason: #{inspect(reason)}")
				[]
			[] ->
				[]
			[rows] ->
				rows
			err ->
				Logger.error("Deepr List Pg Execute Error, reason: #{inspect(err)}")
				[]
		end
	end

	def query(caller_mod, user, object, args, repo \\ nil) do
		case args do
			{m, f, a} ->
				apply(m, f, a)
			_ ->
				mod = caller_mod.get_db_mod(object)
				if mod.db_type() == Pango.DbType.pg() do
					Pango.DeeprPsql.query(caller_mod, user, object, args, repo)
				else
					{v, params} = Pango.DeeprCypherV2.list(caller_mod, user, object, args)
					cypher_execute(v, params, %{}, user, [], caller_mod)
				end
		end
	end

	defp take_rel(object, args) do
		Map.take(Map.get(args, object), ["optional", "match"])
	end

	def get_select(object, args) do
		MapLib.get_in(args, [object, "=>", object])
	end

	def get_rel(mod, object, args, key) do
		case mod.db_type() == Pango.DbType.pg() do
			true ->
				case MapLib.get_in(args, [object, key]) do
					v when is_map(v) ->
						v
					_ ->
						%{}
				end
			_ ->
				case MapLib.get_in(args, [object, "=>", object]) do
					v when is_map(v) ->
						v
					_ ->
						%{}
				end
		end
	end

end
